package queue

import (
	"dmsv/agent"
	"dmsv/session"
	"errors"
	"fmt"
	"github.com/beego/beego/v2/core/logs"
	"sync"
	"time"
)

var gQueue map[string][]*Queue
var gQueLock sync.RWMutex

type enCallInfo struct {
	call        *session.SessionInfo
	lastService string
}

type Queue struct {
	Id         int                //队列Id
	appId      string             //关联公司
	name       string             //队列名称
	key        string             //绑定按键
	media      string             //入队等待音
	qLen       int                //队列长度
	acd        DistribType        //分发方式
	companyAcd DistribType        //企业分发方式
	activeFrom time.Time          //生效起始时间
	activeEnd  time.Time          //生效结束时间
	waitTime   int                //入队后最长等待时间
	callNumber string             //队列绑定号码
	call       chan enCallInfo    //呼入会话
	position   int                //坐席游标
	Agent      []*agent.AgentInfo //签入坐席
	eventChan  chan *Event        //排队事件队列
}

func (q *Queue) distrib() DistribType {

	/*
		if q.acd != "" {
			return q.acd
		}
	*/
	return q.companyAcd
}

func (q *Queue) callEnQueue(sesion *session.SessionInfo, lastService string) bool {

	if len(q.call) >= q.qLen {
		return false
	}

	logs.Debug("[%v] 入队成功,技能组名称 [%v] 接听策略:[%v] 队列长度:[%v],上次服务坐席:%v.",
		sesion.GetSessionId(), q.name, q.distrib(), len(q.call), lastService)

	sesion.UpdateEnQueTime(q.Id, q.name)

	//呼叫中心业务,发送入队成功通知
	ev := &Event{
		Name:    QUE_EVENT_SUCCESS,
		Ssion:   sesion,
		QueName: q.name,
		QueId:   fmt.Sprintf("%d", q.Id),
	}
	q.eventChan <- ev

	nCall := enCallInfo{
		call:        sesion,
		lastService: lastService,
	}

	q.call <- nCall

	return true
}

func (q *Queue) notFull() bool {
	if len(q.call) < q.qLen {
		return true
	} else {
		return false
	}
}

func (q *Queue) getLastService(enCall enCallInfo) bool {

	if len(enCall.lastService) > 0 {
		for _, service := range q.Agent {
			if service.ServiceId() == enCall.lastService && service.IsFree() && service.OutOfAnswerTime() {
				logs.Debug("[%v] 找到上次的空闲坐席.", enCall.call.GetServiceId())
				ev := &Event{
					Name:    QUE_EVENT_FIND_SERVICE,
					Service: service,
					Key:     q.key,
					Ssion:   enCall.call,
					QueName: q.name,
				}
				q.eventChan <- ev
				return true
			}
		}
	}

	return false
}

func (q *Queue) queueProcess(appId string) {

	logs.Debug("appId[%v],queue name[%v]  start queue task", appId, q.name)
	for {

		n, ok := <-q.call
		if ok == false {
			logs.Debug("companyId[%v] que[%v],is delete", appId, q.name)
			return
		}

		newCalls := n.call
		//计算超时时间
		in := newCalls.GetEnQueTime()
		timeOut := in.Add(time.Duration(q.waitTime) * time.Second)

		logs.Debug("appId[%v] que[%v],new call in:%v 入队时间:%v,超时时间:%v,上次服务坐席:%v",
			appId, q.name, newCalls.GetSessionId(), in.Format("15:04:05.000"),
			timeOut.Format("15:04:05.000"), n.lastService)

		//同步坐席状态
		agent.LoadServiceStatus(q.appId)

		find := q.getLastService(n)
		if find {
			continue
		}

		//查找坐席并接听
		service, err := q.GetFreeService(timeOut, newCalls)
		if err != nil {

			logs.Debug("%v queue get free service err:%v", newCalls.GetSessionId(), err)

			//超时的情况，需要发送超时事件
			if false == newCalls.IsEnd() {
				//超时错误处理
				ev := &Event{
					Name:    QUE_EVENT_TIME_OUT,
					Key:     q.key,
					Ssion:   newCalls,
					QueName: q.name,
				}
				q.eventChan <- ev
			}

			continue
		}

		logs.Debug("queue %v get service:%v", q.name, service.ServiceId())
		ev := &Event{
			Name:    QUE_EVENT_FIND_SERVICE,
			Service: service,
			Key:     q.key,
			Ssion:   newCalls,
			QueName: q.name,
		}
		q.eventChan <- ev
	}
}

//签入
func (q *Queue) SignIn(service *agent.AgentInfo) {

	logs.Debug("[%v]service:%v,服务号:%v 签入技能组 id:%v, 名称：%v.", q.appId, service.ServiceId(), service.GetServeNumber(), q.Id, q.name)
	for _, s := range q.Agent {
		//判断重复坐席
		if s.ServiceId() == service.ServiceId() {
			return
		}
	}

	q.Agent = append(q.Agent, service)
}

//获取坐席
func (q *Queue) GetService(serviceId string) *agent.AgentInfo {

	for _, a := range q.Agent {
		if a.ServiceId() == serviceId {
			return a
		}
	}

	return nil
}

//签出
func (q *Queue) SignOut(service *agent.AgentInfo) {

	logs.Debug("service:%v sign out from queue %v-%v", service.ServiceId(), q.appId, q.name)

	for i, a := range q.Agent {
		if a.ServiceId() == service.ServiceId() {

			logs.Info("service:%v sign out from queue %v-%v success", service.ServiceId(), q.appId, q.name)
			q.Agent = append(q.Agent[:i], q.Agent[i+1:]...)
			break
		}
	}
}

//获取空闲坐席
func (q *Queue) GetFreeService(timeOut time.Time, ss *session.SessionInfo) (*agent.AgentInfo, error) {

	count := 0

	for timeOut.After(time.Now()) {

		if ss.IsEnd() {
			return nil, fmt.Errorf("call is end")
		}

		if count%10 == 0 {

			logs.Debug("%v 超时判断:%v,坐席数量:%v, 分发策略:%v,剩余超时:%v,", ss.GetSessionId(),
				timeOut.After(time.Now()), len(q.Agent), q.distrib(), time.Since(timeOut))
		}
		count++

		switch q.distrib() {

		case QUE_DISTRIB_BY_EQUEL:
			//平均:按接听次数
			result := agent.NewBlankAgent()
			for _, cs := range q.Agent {

				logs.Debug("service %v,number:%v status:%v,phone status:%v,answerCount:%v,answerTime:%v",
					cs.ServiceId(), cs.Number(), cs.Status(), cs.PhoneStatus(), cs.AnswerCount(), cs.CallTime())

				if cs.IsFree() && result.AnswerCount() < cs.AnswerCount() && cs.OutOfAnswerTime() {
					result = cs
				}
			}

			if !result.IsBlank() {
				defer result.SetPhoneBusy()
				return result, nil
			}
		case QUE_DISTRIB_BY_ANSWER_TIME:

			//最短接听时长空闲坐席
			result := agent.NewBlankAgent()
			for _, cs := range q.Agent {

				logs.Debug("service %v,number:%v status:%v,phone status:%v,answerCount:%v,answerTime:%v,nextAnswer:%v",
					cs.ServiceId(), cs.Number(), cs.Status(), cs.PhoneStatus(),
					cs.AnswerCount(), cs.CallTime(), cs.NextAnswerTime())

				if cs.IsFree() && result.CallTime() > cs.CallTime() && cs.OutOfAnswerTime() {
					result = cs
				}
			}

			if !result.IsBlank() {
				defer result.SetPhoneBusy()
				return result, nil
			}

		case QUE_DISTRIB_BY_ANSWER_ACCOUNT:
			result := agent.NewBlankAgent()
			for _, cs := range q.Agent {

				logs.Debug("service %v,number:%v status:%v,phone status:%v,answerCount:%v,answerTime:%v",
					cs.ServiceId(), cs.Number(), cs.Status(), cs.PhoneStatus(), cs.AnswerCount(), cs.CallTime())

				if cs.IsFree() && result.AnswerCount() > cs.AnswerCount() && cs.OutOfAnswerTime() {
					result = cs
				}
			}

			if !result.IsBlank() {
				defer result.SetPhoneBusy()
				return result, nil
			}

		default:

			//根据签入顺序获取空闲坐席
			for i, service := range q.Agent {

				logs.Debug("index :%v service %v,number:%v status:%v,phone status:%v,answerCount:%v,answerTime:%v",
					i, service.ServiceId(), service.Number(), service.Status(), service.PhoneStatus(),
					service.AnswerCount(), service.CallTime())

				if service.IsFree() && service.OutOfAnswerTime() {
					defer service.SetPhoneBusy()

					return service, nil
				}
			}
		}

		time.Sleep(1 * time.Second)
	}

	return nil, errors.New("timeOut")
}

func addNewQue(appId string, nq *Queue) {

	gQueLock.Lock()
	defer gQueLock.Unlock()

	que, ok := gQueue[appId]
	if !ok {
		que = make([]*Queue, 0, 0)
	}

	que = append(que, nq)
	gQueue[appId] = que
	return
}

func getQueHandleByName(appId, name string) (*Queue, error) {

	gQueLock.RLock()
	defer gQueLock.RUnlock()

	que, ok := gQueue[appId]
	if !ok {
		return nil, errors.New("no appId info")
	}

	for i, q := range que {
		logs.Debug("key:%v 队列名称:%v", i, q.name)
		if q.name == name {
			return q, nil
		}
	}
	return nil, errors.New("queue not found")
}

func getQueHandle(appId string, queId int) (*Queue, error) {

	gQueLock.RLock()
	defer gQueLock.RUnlock()

	que, ok := gQueue[appId]
	if !ok {
		return nil, errors.New("no appId info")
	}

	for _, q := range que {
		if q.Id == queId {
			return q, nil
		}
	}
	return nil, errors.New("queue not found")
}

func getAllQueHandle(companyId string) ([]*Queue, error) {

	gQueLock.RLock()
	defer gQueLock.RUnlock()

	que, ok := gQueue[companyId]
	if !ok {
		return nil, errors.New("no appId info")
	}

	return que, nil
}

func convertStrToDistrbType(input string) DistribType {

	switch input {

	case "index":
		return QUE_DISTRIB_BY_INDEX

	case "level":
		return QUE_DISTRIB_BY_LEVEL

	case "proportion":
		return QUE_DISTRIB_BY_PROPORTION

	case "freeTime":
		return QUE_DISTRIB_BY_ANSWER_TIME

	case "answerTime":
		return QUE_DISTRIB_BY_ANSWER_TIME

	case "answerCount":
		return QUE_DISTRIB_BY_ANSWER_ACCOUNT

	default:
		return QUE_DISTRIB_NULL
	}
}

func convertStrToDistrbTypeDefault(input string) DistribType {

	switch input {

	case "index":
		return QUE_DISTRIB_BY_INDEX

	case "level":
		return QUE_DISTRIB_BY_LEVEL

	case "proportion":
		return QUE_DISTRIB_BY_PROPORTION

	case "freeTime":
		return QUE_DISTRIB_BY_ANSWER_TIME

	case "answerTime":
		return QUE_DISTRIB_BY_ANSWER_TIME

	case "answerCount":
		return QUE_DISTRIB_BY_ANSWER_ACCOUNT

	default:
		return QUE_DISTRIB_BY_INDEX
	}
}
